package elijah.base.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

import androidx.annotation.Nullable;

import java.util.Locale;

import elijah.base.R;

public class WaterProgressView extends View {

    public int waveCount = 3;

    float waveHeight = 10;

    public static final int MODE_CONSTRAINT = 0;
    public static final int MODE_FULL = 1;
    private int mode = MODE_FULL;

    private Paint paint;
    private int radius;
    private ValueAnimator valueAnimator;
    private RectF rectF;
    private Path circlePath;

    private float rate = 0.55f;

    private int startColor;
    private int endColor;

    private int width, height;

    private int labelTextColor = Color.WHITE;
    private float labelTextSize = 72;
    private TextPaint textPaint;
    private boolean showLabel;
    private String labelFormat = "%.1f%%";

    public WaterProgressView(Context context) {
        this(context, null);
    }

    public WaterProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WaterProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        if (attrs != null) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaterProgressView);
            startColor = typedArray.getColor(R.styleable.WaterProgressView_startColor, 0xff00ff96);
            endColor = typedArray.getColor(R.styleable.WaterProgressView_endColor, 0xff108d62);
            mode = typedArray.getInt(R.styleable.WaterProgressView_waveMode, MODE_FULL);
            waveCount = typedArray.getInt(R.styleable.WaterProgressView_waveCount, 3);
            waveHeight = typedArray.getDimension(R.styleable.WaterProgressView_waveHeight, 10);
            labelTextColor = typedArray.getColor(R.styleable.WaterProgressView_labelTextColor, labelTextColor);
            labelTextSize = typedArray.getDimension(R.styleable.WaterProgressView_labelTextSize, labelTextSize);
            showLabel = typedArray.getBoolean(R.styleable.WaterProgressView_showLabel, false);
            labelFormat = typedArray.getString(R.styleable.WaterProgressView_labelFormat);
            if (TextUtils.isEmpty(labelFormat)) {
                labelFormat = "%.1f%%";
            }
            typedArray.recycle();
        }


        init();
    }

    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectF = new RectF();
        circlePath = new Path();
        initTextPaint();
    }

    private void initTextPaint() {
        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setColor(labelTextColor);
        textPaint.setTextSize(labelTextSize);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setFakeBoldText(true);
    }

    public WaterProgressView setRate(float rate) {
        if (rate == this.rate) {
            return this;
        }
        this.rate = Math.min(Math.max(rate, 0), 1);
        postInvalidate();
        return this;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        valueAnimator = ValueAnimator.ofFloat(1f, 0f, -1f).setDuration(1000);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        valueAnimator.addUpdateListener(animation -> {
            waveHeight = (float) animation.getAnimatedValue();
            postInvalidate();

        });
        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
        valueAnimator.setRepeatCount(-1);


        valueAnimator.start();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        valueAnimator.cancel();

    }

    RadialGradient radialGradient;
    RadialGradient radialGradient2;
    LinearGradient linearGradient;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.translate(width / 2f, height / 2f);


        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(12);
        paint.setShader(radialGradient);
        canvas.drawCircle(0, 0, radius - 12, paint);

        paint.setShader(null);
        paint.setStrokeWidth(4);
        paint.setColor(0x4420ffdd);
        canvas.drawCircle(0, 0, radius - 16, paint);

        paint.setShader(null);
        paint.setShader(radialGradient2);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(0, 0, radius - 32, paint);
        circlePath.reset();
        circlePath.addCircle(0, 0, radius - 32, Path.Direction.CCW);

        paint.setShader(null);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);

        if (mode == MODE_CONSTRAINT) {
            float waveRadius = radius - 32;
            float y = ((waveRadius) - rate * waveRadius * 2);
            float x = (float) Math.sqrt(waveRadius * waveRadius - y * y);
            float startAngle = (float) Math.toDegrees(Math.asin(y / waveRadius));
            float sweepAngle = (90 - startAngle) * 2;
            rectF.set(-waveRadius, -waveRadius, waveRadius, waveRadius);
            paint.setAlpha(120);
            drawWave(canvas, x, y, startAngle, sweepAngle, 1);
            paint.setAlpha(255);
            drawWave(canvas, x, y, startAngle, sweepAngle, -1);
        } else if (mode == MODE_FULL) {
            float waveRadius = radius;
            float y = ((waveRadius) - rate * waveRadius * 2);
            float x = (float) Math.sqrt(waveRadius * waveRadius - y * y);
            float startAngle = (float) Math.toDegrees(Math.asin(y / waveRadius));
            float sweepAngle = (90 - startAngle) * 2;
            rectF.set(-waveRadius, -waveRadius, waveRadius, waveRadius);

            paint.setAlpha(120);
            drawFullWave(canvas, x, y, startAngle, sweepAngle, 1);
            paint.setAlpha(255);
            drawFullWave(canvas, x, y, startAngle, sweepAngle, -1);
        }


        if (showLabel) {
            drawLabel(canvas);
        }
    }

    private void drawLabel(Canvas canvas) {

        String text = String.format(Locale.CHINA, labelFormat, rate * 100);
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
        canvas.drawText(text, 0, distance, textPaint);
    }

    private void drawWave(Canvas canvas, float x, float y, float startAngle, float sweepAngle, int direction) {
        path.reset();
        path.moveTo(x, y);
        path.addArc(rectF, startAngle, sweepAngle);

        float step = 2 * x / waveCount;
        float start = -x;

        for (int i = 0; i < waveCount; i += 1) {
            path.quadTo(start + 1f / 2 * step, y - (waveHeight) * (x / 8f) * (i % 2 == 0 ? 1 : -1) * direction, start + step, y);
            start += step;
        }
        paint.setShader(linearGradient);
        canvas.drawPath(path, paint);
    }

    private void drawFullWave(Canvas canvas, float x, float y, float startAngle, float sweepAngle, int direction) {
        path.reset();
        path.moveTo(x, y);
        path.addArc(rectF, startAngle, sweepAngle);

        float step = 2 * x / waveCount;
        float start = -x;

        for (int i = 0; i < waveCount; i += 1) {
            path.quadTo(start + 1f / 2 * step, y - (waveHeight) * (x / 8f) * (i % 2 == 0 ? 1 : -1) * direction, start + step, y);
            start += step;
        }
        paint.setShader(linearGradient);
        path.op(circlePath, Path.Op.INTERSECT); // 裁剪为圆形区域内
        canvas.drawPath(path, paint);
    }

    Path path = new Path();

    @Override
    protected void onSizeChanged(int w, int h, int oldW, int oldH) {
        super.onSizeChanged(w, h, oldW, oldH);
        radius = Math.min(w, h) / 2;
        linearGradient = new LinearGradient(radius, radius, radius, 0, new int[]{endColor, startColor}, null, Shader.TileMode.CLAMP);
        radialGradient = new RadialGradient(0, 0, radius - 12, new int[]{0xff20ffdd, 0x2220ffdd}, null, Shader.TileMode.CLAMP);
        radialGradient2 = new RadialGradient(0, 0, radius - 32, new int[]{0x33ffffff, 0xffffffff}, new float[]{0.75f, 1f}, Shader.TileMode.CLAMP);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);
        radius = Math.min(width, height) / 2;

    }
}
